#!/bin/bash
# Build linux system and generate ISO
# Version: 0.5.0
# (C) Chris Dorman, 2014-2020 - GPLv3+

workdir=`pwd`
freondir="/freon"
corecount=25

# Common mirror (now self hosted)
mainmirror="https://mirror.freonlinux.com/source"

# Download mirrors
syslinuxmirror="https://www.kernel.org/pub/linux/utils/boot/syslinux"
kernelmirror="https://cdn.kernel.org/pub/linux/kernel/v5.x"
busyboxmirror="http://busybox.net/downloads"
libcmirror="http://ftp.gnu.org/gnu/libc"
grubmirror="http://alpha.gnu.org/gnu/grub"
xzmirror="https://tukaani.org/xz"
dropbearmirror="https://matt.ucc.asn.au/dropbear/releases"
zlibmirror="https://zlib.net"


# endtag used for iso filename, and some rootfs configs
endtag=`date +"%Y%m%d-%H%M"`
hostbuild="Debian 10"

# Source code filenames
kernel="linux-5.9.1.tar.xz"
syslinux="syslinux-6.03.tar.gz"
busybox="busybox-1.32.0.tar.bz2"
libc="glibc-2.28.tar.xz"
grub="grub-2.02~rc2.tar.xz"
xz="xz-5.2.4.tar.xz"
dropbear="dropbear-2019.78.tar.bz2"
zlib="zlib-1.2.11.tar.xz"


# Source directory names
kerneldir=${kernel//.tar.xz}
busyboxdir=${busybox//.tar.bz2}
syslinuxdir=${syslinux//.tar.gz}
libcdir=${libc//.tar.xz}
grubdir=${grub//.tar.xz}
xzdir=${xz//.tar.xz}
dropbearsrcdir=${dropbear//.tar.bz2}
zlibsrcdir=${zlib//.tar.xz}

# Echo colors
NORMAL="\e[0m"
RED="\e[0;31m"
GREEN="\e[0;32m"
BLUE="\e[0;34m"
YELLOW="\e[1;33m"
MAGENTA="\e[0;35m"
CYAN="\e[0;36m"

# function to check if errors occurred
status()
{
  local CHECK=$?
    echo -en "\033[68G"
    if [ $CHECK = 0 ] ; then
      echo -e "[ \e[0;32mOK\e[0m ]"
    else
      echo -e "[ \e[0;31mFAILED\e[0m ]"
      exit 1
    fi
}

# same as above but doesn't output [ OK ]
status_silent()
{
  local CHECK=$?
    echo -en "\033[68G"
    if [ $CHECK != 0 ] ; then
      echo -e "[ \e[0;31mFAILED\e[0m ]"
      exit 1
    fi
}

if [ "$(id -u)" != "0" ]; then
  echo -e "[$RED Error $NORMAL] This script needs to be executed by root."
  exit 1
fi

if [ ! -d "src" ]; then
  echo -e "[$YELLOW Warning $NORMAL] No source directory!"
  echo -n "Creating source directory..."
  mkdir src > /dev/null 2>&1
  status
fi

# check to see if source dir exists, if not create
if [ ! -d src ]; then
	mkdir src
	status_silent
fi

# if called, print everything instead of hiding. Debugging purposes
case $2 in
  --verbose|-v) verbose=1;;
  *) verbose=0;
esac

# Download source code
get_files()
{

  cd $workdir/src
	
  if [ -d "rootfs" ]; then
    echo -e "[$YELLOW Warning $NORMAL] Rootfs tree still exists."
    echo -n "Cleaning build tree..."
    rm -r rootfs > /dev/null 2>&1
    status
  fi

  #echo " " # This is used to keep next echo from moving around
  echo -e "[$YELLOW Working $NORMAL] Downloading needed files..."
  if [ ! -f $kernel ]; then
	echo -n "$kernel..."
    wget $mainmirror/$kernel > /dev/null 2>&1
    status
  fi 
  
  if [ ! -f $busybox ]; then
    echo -n "$busybox..."
    wget $mainmirror/$busybox > /dev/null 2>&1
    status
  fi
  
  if [ ! -f $syslinux ]; then
    echo -n "$syslinux..."
    wget $mainmirror/$syslinux > /dev/null 2>&1
    status
  fi
  
  if [ ! -f $xz ]; then
    echo -n "$xz..."
    wget $mainmirror/$xz > /dev/null 2>&1
    status
  fi

  if [ ! -f $libc ]; then
    echo -n "$libc..."
    wget $mainmirror/$libc > /dev/null 2>&1
    status
  fi
  
  if [ ! -f $grub ]; then
    echo -n "$grub..."
    wget $mainmirror/$grub > /dev/null 2>&1
    status
  fi
  
}

# Build linux kernel
do_kernel()
{

  cd $workdir/src

  echo -e "[$YELLOW Working $NORMAL] Configuring & building Linux kernel"
  if [ ! -d $kerneldir ]; then
    echo -n "Unpacking..."
    tar xf $kernel > /dev/null 2>&1
    status
  fi

  echo -n "Configuring..."
  cd $kerneldir
  cp ../../files/$kerneldir.conf .config
  status

  echo -n "Building kernel base..."
  if [ "$verbose" == "1" ]; then
    make bzImage -j$corecount
    status
  else
    make bzImage -j$corecount > /dev/null 2>&1
    status
  fi

  #echo -n "Building kernel modules..."
  #if [ "$verbose" == "1" ]; then
  #  make modules -j20
  #  status
  #else
  #  make modules -j20 > /dev/null 2>&1
  #  status
  #fi
  
  #echo -n "Installing kernel modules..."
  #make INSTALL_MOD_PATH=`pwd`/_pkg modules_install > /dev/null 2>&1
  #status
  
  echo -n "Installing kernel headers..."
  make INSTALL_HDR_PATH=`pwd`/_hdr headers_install > /dev/null 2>&1
  status
  
  cd ..

}

# Build busybox
do_base()
{

  echo -e "[$YELLOW Working $NORMAL] Configuring & building system utilities"
  
  cd $workdir/src
  
  if [ ! -d $busyboxdir ]; then
    echo -n "Unpacking..."
    tar xf $busybox > /dev/null 2>&1
    status
  fi

  echo -n "Configuring..."
  cd $busyboxdir
  cp ../../files/$busyboxdir.conf .config
  status_silent
  
  make oldconfig > /dev/null 2>&1
  status

  echo -n "Building..."
  if [ "$verbose" == "1" ]; then 
    make -j$corecount
    status
  else
    make -j$corecount > /dev/null 2>&1
    status
  fi
  
  if [ "$verbose" == "1" ]; then
    make install
    status
  else
    make install > /dev/null 2>&1
    status
  fi
  
  echo -n "Finishing up..."
  chmod 4755 _install/bin/busybox
  cp -a _install/* /freon
  status
  
  cd $workdir
  
  cd system/ii
  
  make -j$corecount; make install
  status_silent
  
  cd ../chttpd
  
  cp inc/chttpd ../../rootfs/etc/init.d/chttpd
  
  make -j$corecount; make install
  status_silent
  
  cd ../sic
  
  make -j$corecount; make install
  status_silent
  
  cd $workdir
  
}

# Build system wide dependencies
do_libc()
{
  echo -e "[$YELLOW Working $NORMAL] Configuring & building libc"
  
  cd $workdir/src
  
  if [ ! -d $libcdir ]; then
    echo "Unpacking..."
    tar xf $libc > /dev/null 2>&1
    status
  fi
  
  if [ ! -d rootfs ]; then
    mkdir rootfs
    
    cd rootfs/
    
    echo -n "Laying out filesystem tree..."
    mkdir -p dev root etc home proc media mnt sys tmp var
    mkdir -p usr/{lib,local,share,games} \
    var/{cache,lib,lock,log,games,run,spool,www} \
    media/{cdrom,flash,usbdisk} \
    etc/{init.d,conf.d}
    
    cd ..
  fi
  
  if [ ! -d $libcdir-build ]; then
    mkdir $libcdir-build
  fi
  
  if [ ! -d /freon/lib ]; then
    mkdir /freon/lib
  fi
  
  cd $libcdir-build
  
  echo -n "Configuring..."
  if [ "$verbose" == "1" ]; then
	`pwd`/../$libcdir/configure \
    --prefix=$freondir \
    --build=$MACHTYPE \
    --host=x86_64-linux \
    --target=x86_64-linux 
    --with-headers=_hdr/include
    status
  else
    `pwd`/../$libcdir/configure \
    --prefix=/freon \
    --build=$MACHTYPE \
    --host=x86_64-linux \
    --target=x86_64-linux > /dev/null 2>&1
    --with-headers=_hdr/include > /dev/null 2>&1
    status
  fi
  
  echo -n "Building..."
  if [ "$verbose" == "1" ]; then
    make install-bootstrap-headers=yes install-headers
    status
    
    make -j$corecount csu/subdir_lib
    status
    
    install csu/crt1.o csu/crti.o csu/crtn.o /freon/lib
    status
    
    gcc \
	-nostdlib \
	-nostartfiles -shared -x c /dev/null -o $freondir/lib/libc.so
    status
    
    make -j$corecount
    status
  else
    make install-bootstrap-headers=yes install-headers > /dev/null 2>&1
    status_silent
    
    make -j$corecount csu/subdir_lib > /dev/null 2>&1
    status_silent
    
    install csu/crt1.o csu/crti.o csu/crtn.o $freondir/lib > /dev/null 2>&1
    status_silent
    
    gcc \
	-nostdlib \
	-nostartfiles -shared -x c /dev/null -o $freondir/lib/libc.so 2>&1
    status_silent
    
    make -j$corecount > /dev/null 2>&1
    status
  fi
  
  echo -n "Copying to rootfs..."
  if [ "$verbose" == "1" ]; then
    make install
    status
  else
    make install > /dev/null 2>&1
    status
  fi
  
  echo -n "Cleaning files..."
  #strip -v lib/* > /dev/null 2>&1
  
  mkdir $workdir/src/rootfs/lib64
  cd $workdir/src/rootfs/lib64
  
  ln -s $freondir/lib/ld-2.28.so ld-linux-x86-64.so.2
  status
  
  cd $freondir
  
  if [ ! -d "lib64" ]; then
	mkdir lib64
  fi
  
  cd lib64
  
  ln -s $freondir/lib/ld-2.28.so ld-linux-x86-64.so.2

  cd $workdir
  
}

# Build grub legacy bootloader and install
do_grub()
{

  echo -e "[$YELLOW Working $NORMAL] Configuring & building bootloader"
  
  cd $workdir/src
  
  echo -n "Extracting..."
  if [ ! -d "$xzdir" ]; then
        tar xf $xz > /dev/null 2>&1
        status_silent
  fi

  if [ ! -d "$grubdir" ]; then
	tar xf $grub > /dev/null 2>&1
	status
  fi
  
  cd $grubdir
  
  echo -n "Configuring..."
  ./configure --prefix=`pwd`/../rootfs \
			  --disable-efiemu \
			  --disable-werror > /dev/null 2>&1
  status_silent

  cd ../$xzdir

  ./configure --prefix=`pwd`/../rootfs \
            --disable-static > /dev/null 2>&1
  status

  cd ../$grubdir
  
  echo -n "Compiling..."
  make -j$corecount > /dev/null 2>&1
  status_silent

  cd ../$xzdir

  make -j$corecount > /dev/null 2>&1
  status

  cd ../$grubdir

  echo -n "Copying to rootfs..."
  make install > /dev/null 2>&1
  status_silent
  cd ../$xzdir
  make install > /dev/null 2>&1
  status
  cd ../$grubdir
  
  echo -n "Installing patched grub-mkconfig..."
  rm ../rootfs/sbin/grub-mkconfig > /dev/null 2>&1
  status_silent
  cp ../../files/sbin/grub-mkconfig ../rootfs/sbin > /dev/null 2>&1
  status
  
  cd ../rootfs
  
  #echo -n "Stripping grub..."
  #strip -s rootfs/bin/grub-* > /dev/null 2>&1
  #strip -s rootfs/sbin/grub-* > /dev/null 2>&1
  cd ..
  #status 
  
}

# Install Freon utilities
do_utilities() {
  echo -e "[$YELLOW Working $NORMAL] Installing Freon utilities"
  echo -n "Copying files..."
  cd $workdir/src
  
  cp -a ../files/bin/* rootfs/bin
  
  status
  
}

# Configure rootfs for live boot
configure_system()
{
	
  echo -e "[$YELLOW Working $NORMAL] Configuring root filesystem"
  echo -n "Linking init..."
  cd $workdir/src/rootfs
  
  if [ -f linuxrc ]; then
    rm linuxrc
  fi
 
  if [ ! -f init ]; then
    ln -s $freondir/bin/busybox init
  fi
  status
  
  echo -n "Filling /dev..."
  cp ../../files/bin/mkdevs $freondir/bin/mkdevs
  $freondir/bin/mkdevs dev > /dev/null 2>&1
  status
  
  echo -n "Configuring networking..."
  echo "127.0.0.1	localhost" > etc/hosts
  echo "localhost	127.0.0.1" > etc/networks
  echo "linux" > etc/hostname
  echo "order hosts,bind" > etc/host.conf 
  echo "multi on" >> etc/host.conf 
  
  mkdir etc/network
  mkdir etc/network/if-up.d
  mkdir etc/network/if-down.d
  mkdir etc/network/if-pre-up.d
  mkdir etc/network/if-post-down.d
  
  echo "# loopback
auto lo
iface lo inet loopback

# Ethernet // this is default and can be changed for per system
auto eth0
iface eth0 inet dhcp
" > etc/network/interfaces
  
  echo "
# /etc/nsswitch.conf: GNU Name Service Switch config.
#

passwd:     files
group:      files
shadow:     files

hosts:      files dns
networks:   files
" > etc/nsswitch.conf 
  status
  
  echo -n "Setting up default users and groups..."
  echo "root:x:0:0:root:/root:/bin/sh" > etc/passwd
  echo "root::13525:0:99999:7:::" > etc/shadow
  echo "root:x:0:" > etc/group
  echo "root:*::" > etc/gshadow
  chmod 640 etc/shadow
  chmod 640 etc/gshadow
  status
  
  #echo -n "Copying kernel modules..."
  #cp -a ../$kerneldir/_pkg/* .
  #status
  
  #echo -n "Copying kernel headers..."
  #cp -a ../$kerneldir/_hdr/* .
  #status
  
  echo -n "Compiling termtypes for core terminfo...."
  tic -o ./usr/share/terminfo ../../files/terminfo/termtypes > /dev/null 2>&1
  status

  echo -n "Finishing up..."
  echo "# /etc/securetty: List of terminals on which root is allowed to login.
#
console

# For people with serial port consoles
ttyS0

# Standard consoles
tty1
tty2
tty3
tty4
tty5
tty6
tty7" > etc/securetty

  echo "/bin/sh" > etc/shells
  echo "/bin/ash" >> etc/shells
  
  echo "Freon Linux $endtag \r \l" > etc/issue
  echo "" >> etc/issue
  
echo "Welcome to
_________
___  ____/________________________
__  /_   __  ___/  _ \\  __ \\_  __ \\
_  __/   _  /   /  __/ /_/ /  / / /
/_/      /_/    \\___/\\____//_/ /_/ 

Compiled $endtag using $hostbuild" > etc/motd

  echo "[SUID]
# Allow command to be run by anyone.
su = ssx root.root
passwd = ssx root.root
loadkmap = ssx root.root
mount = ssx root.root
reboot = ssx root.root
halt = ssx root.root" > etc/busybox.conf 

  chmod 600 etc/busybox.conf 
  
  echo "::sysinit:/etc/init.d/rc.init
::respawn:-/bin/sh
tty2::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
::ctrlaltdel:/sbin/reboot" > etc/inittab

  echo "# /etc/profile: system-wide .profile file for the Bourne shells

PATH=\"/freon/bin:/freon/sbin:/freon/usr/bin:/freon/usr/sbin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/bin:/usr/local/sbin\"
LD_LIBRARY_PATH=\"/freon/lib:/freon/lib64:/freon/usr/lib:/lib64:/usr/lib:/lib:/usr/local/lib\"
TERM=\"xterm\"
TERMINFO=\"/share/terminfo\"

NORMAL=\"\\[\\e[0m\\]\"
RED=\"\\[\\e[0;31m\\]\"
GREEN=\"\\[\\e[0;32m\\]\"
BLUE=\"\\[\\e[0;34m\\]\"
YELLOW=\"\\[\\e[1;33m\\]\"
MAGENTA=\"\\[\\e[0;35m\\]\"
CYAN=\"\\[\\e[0;36m\\]\"

if [ \"\`id -u\`\" -eq 0 ]; then
  PS1=\"$RED\\u$GREEN@$BLUE\\h [ $MAGENTA\\w$BLUE ]# \$NORMAL\"
else
  PS1=\"$RED\\u$GREEN@$BLUE\\h [ $MAGENTA\\w$BLUE ]\\\$ \$NORMAL\"
fi

export PATH LD_LIBRARY_PATH PS1 DISPLAY ignoreeof
umask 022

export G_FILENAME_ENCODING=iso8859-1
" > etc/profile

  echo "proc            /proc        proc    defaults          0       0
sysfs           /sys         sysfs   defaults          0       0
devpts          /dev/pts     devpts  defaults          0       0
tmpfs           /dev/shm     tmpfs   defaults          0       0" > etc/fstab

  cp -a $workdir/files/etc/* etc
  
  cd share
  mkdir kmap
  
  cp -a $workdir/files/share/* .
  status_silent
  
  chmod +x udhcpc/default.script
  status_silent
  
  cd ../usr/share
  
  ln -s /share/udhcpc udhcpc
  
  cd ../..
  
#  mkdir x86_64-linux-gnu
  
#  cd x86_64-linux-gnu
  
#  ln -s ../libm-2.22.so libm.so.6 
#  ln -s ../libc-2.22.so libc.so.6
  
  status
  
  cd $workdir
  
}

# generate initramfs for live boot.
gen_rootfs()
{
  echo -e "[$YELLOW Working $NORMAL] Generating filesystem..."
  
  cd $workdir/src/rootfs
  
  cp -a $freondir .
  
  cd bin
  
  for f in /freon/bin/*; do
    ln -s $f .
  done
  
  cd ../sbin
  
  for f in /freon/sbin/*; do
    ln -s $f .
  done
  
  cd ../lib
  
  for l in /freon/lib/*.so; do
    ln -nsf $l .
  done
  
  for l in /freon/lib/*.so*; do
    ln -nsf $l .
  done
  
  for l in /freon/lib/*.o; do
    ln -nsf $l .
  done
  
  cd ..
  
  echo -n "Stripping build..."
  strip -s freon/lib/*.so > /dev/null 2>&1
  strip -s freon/lib/*.so* > /dev/null 2>&1
  strip -s freon/bin/busybox > /dev/null 2>&1
  strip -s freon/sbin/chttpd > /dev/null 2>&1
  rm freon/lib/*.a
  rm lib/*.a # In base-dev package
  rm usr/lib/*.a 
  
  echo "#!/bin/sh
exec /freon/bin/sh \$*" > bin/bash

  chmod +x bin/bash
  
  echo -n "Compressing filesystem..."
  find . -print | cpio -o -H newc | gzip -9 > ../rootfs.gz 2>&1
  status
  
  cd $workdir
  
}

regen_rootfs_bare()
{
  echo -e "[$YELLOW Working $NORMAL] Generating filesystem..."
  
  cd $workdir/src/rootfs
  
  cp -a $freondir .
  
  echo -n "Compressing filesystem..."
  find . -print | cpio -o -H newc | gzip -9 > ../rootfs.gz 2>&1
  status
  
  cd $workdir
}

# setup bootloader for iso
do_isolinux()
{
  echo -e "[$YELLOW Working $NORMAL] Setting up bootloader."
  
  cd $workdir/src/
  
  if [ ! -d $syslinuxdir ]; then
    echo -n "Unpacking $syslinuxdir..."
    tar -xzf $syslinux > /dev/null 2>&1
    status
  fi

  if [ ! -d cdroot ]; then
    mkdir cdroot
    mkdir cdroot/boot
  fi
  
  cd $syslinuxdir
	
  echo -n "Copying isolinux files to iso..."
  cp bios/core/isolinux.bin ../cdroot
  cp bios/com32/elflink/ldlinux/ldlinux.c32 ../cdroot 
  status
	
  echo -n "Generating isolinux config..."
  echo "Freon Linux $endtag
Press <enter> to boot" > ../cdroot/display.txt
  echo "display display.txt
default Freon
label Freon
    kernel /boot/bzImage
    append initrd=/boot/rootfs.gz rw root=/dev/null vga=788
implicit 0
prompt 1
timeout 5" > ../cdroot/isolinux.cfg
  status

  cd $workdir
}

# generate complete iso ready for boot
gen_iso() 
{
  
  echo -e -n "[$YELLOW Working $NORMAL] Generating bootable ISO image."
	
  cd $workdir/src
	
  cp rootfs.gz cdroot/boot
  cp $kerneldir/arch/x86/boot/bzImage cdroot/boot/bzImage
	
  genisoimage -R -o ../freon-$endtag.iso -b isolinux.bin \
  -c boot.cat -no-emul-boot -boot-load-size 4 \
  -V "Freon Linux" -input-charset iso8859-1 -boot-info-table cdroot > /dev/null 2>&1
  status
   
}

do_refer()
{
  echo -e "[$YELLOW Working $NORMAL] Reconfiguring root filesystem"
  
  cd $workdir/src/rootfs
  
  rm etc/init.d/*
  rm etc/conf.d/*
  cp -av ../../files/etc/* etc/ # Copy files/etc to filesystem (init scripts)
  cp -av ../../files/bin/* bin/ # Copy installer and package manager
  cp -av ../../files/share/* share/ # Stuffs

  cd $workdir
}


do_dropbear()
{
	
	cd $workdir/src
	
	# Downloading dropbear source
	if [ ! -f "$dropbear" ]; then
		echo "Downloading Dropbear sources..."
		wget $dropbearmirror/$dropbear
		tar -xf $dropbear
	fi

	# Configure dropbear
	cd $dropbearsrcdir

	cp -v options.h options.h.backup
	sed -e "s@/dev/random@/dev/urandom@" options.h.backup > options.h
	./configure --prefix=$workdir/src/rootfs

	# Compile
	make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" MULTI=1
	make MULTI=1 PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" install

	mkdir -pv $workdir/src/rootfs/etc/dropbear
	rm $workdir/src/rootfs/sbin/dropbear
	rm $workdir/src/rootfs/bin/{dropbearconvert,dropbearkey,scp,dbclient}

	cd $workdir/src/rootfs/bin

	ln -svf dropbearmulti dropbear
	ln -svf dropbearmulti dbclient
	ln -svf dropbearmulti dropbearkey
	ln -svf dropbearmulti dropbearconvert
	ln -svf dropbearmulti scp

    cd $workdir
}

do_zlib()
{
	cd $workdir/src
	
	# Downloading zlib source
	if [ ! -f "$zlib" ]; then
		echo "Downloading zlib sources..."
		wget $zlibmirror/$zlib
		tar -xf $zlib
	fi

	# Confingure
	cd $zlibsrcdir
	./configure --prefix=$freondir

	# Compile
	make -j$corecount
	make install

	# mv -v ../../src/rootfs/usr/lib/libz.so.* ../../src/rootfs/lib
	# ln -sfv ../../src/rootfs/lib/libz.so ../../src/rootfs/usr/lib/libz.so

    cd $workdir
}


case $1 in
	rootfs) get_files; do_libc; do_base; do_grub; do_zlib; do_dropbear; do_kernel; configure_system; do_utilities;;
	refer)  do_refer; regen_rootfs_bare; do_isolinux; gen_iso;;
	full) get_files; do_libc; do_base; do_grub; do_zlib; do_dropbear; do_kernel; configure_system; do_utilities; gen_rootfs; do_isolinux; gen_iso;;
	*) echo "Unknown argument: ./build-freon [full|refer|rootfs]";;
esac
